Django表单集合 您所在的位置:网站首页 formset failed to load storage Django表单集合

Django表单集合

2023-12-27 08:47| 来源: 网络整理| 查看: 265

一、Formset简介

Formset(表单集)是多个表单的集合。Formset在Web开发中应用很普遍,它可以让用户在同一个页面上提交多张表单,一键添加多个数据,比如一个页面上添加多个用户信息。

Django针对不同的formset提供了三种方法:formset_factory、modelformset_factory和inlineformset_factory。

二、formset_factory

对于继承forms.Form的自定义表单,我们可以使用formset_factory。

#forms.py from django.forms import formset_factory from django import forms class BookForm(forms.Form): name = forms.CharField(max_length=100) title = forms.CharField() pub_date = forms.DateField(required=False) BookFormSet = formset_factory(BookForm, extra=3, max_num=2) # extra: 想要显示空表单的数量 # max_num: 表单显示最大数量,可选,默认1000

在视图文件views.py里,我们可以像使用form一样使用formset

# views.py - formsets example. from .forms import BookFormSet from django.shortcuts import render def manage_books(request):    if requesthod == 'POST':        formset = BookFormSet(request.POST)        if formset.is_valid():            # do something with the formset.cleaned_data            pass    else:        formset = BookFormSet() #如果想传入初始数据可设置initial = [{'name':'python','pub_date':'北京出版社'}]    return render(request, 'manage_books.html', {'formset': formset})

注意:如果使用了 initial 来显示formset,那么您需要在处理formset提交时传入相同的 initial ,以便formset检测用户更改了哪些表单。例如,您可能有这样的: BookFormSet(request.POST, initial=[...])。

模板里可以这样使用formset:

{{ formset }}

也可以这样使用:

   {{ formset.management_form }} #一定要加这行代码            {% for form in formset %}        {{ form }}        {% endfor %}    

formset_factory()参数解释:

1、如果 max_num 的值大于初始数据现有数量,那空白表单可显示的数量取决于 extra 的数量,只要总表单数不超过 max_num 。例如, extra=2 , max_num=2 并且formset有一个 initial 初始化项,则会显示一张初始化表单和一张空白表单。

2、如果初始数据项的数量超过 max_num ,那么 max_num 的值会被无视,所有初始数据表单都会显示,并且也不会有额外的表单显示。例如,假设 extra=3 , max_num=1 并且formset有两个初始化项,那么只会显示两张有初始化数据的表单。

3、max_num 的值 None (默认值),它限制最多显示(1000)张表单,其实这相当于没有限制。

三、modelformset_factory

Formset也可以直接由模型model创建,这时你需要使用modelformset_factory。可以指定需要显示的字段和表单数量。由ModelForm创建formset:

from django.forms import modelformset_factory from myapp.models import StudentStudyRecord class StudentStudyRecordModelForm(forms.ModelForm): class Meta: model=StudentStudyRecord fields=["score","homework_note"] model_formset_cls=modelformset_factory (model=StudentStudyRecord,form=StudentStudyRecordModelForm,extra=0)

views.py

class RecordScoreView(View): def get(self, request,class_study_record_id): model_formset_cls=modelformset_factory(model=StudentStudyRecord,form=StudentStudyRecordModelForm,extra=0) queryset = StudentStudyRecord.objects.filter(classstudyrecord=class_study_record_id) formset = model_formset_cls(queryset=queryset) return render(request,"student/record_score.html",locals()) def post(self, request,class_study_record_id): model_formset_cls = modelformset_factory(model=StudentStudyRecord, form=StudentStudyRecordModelForm, extra=0) formset=model_formset_cls(request.POST) if formset.is_valid(): formset.save() return redirect(request.path)

模板:

         {% csrf_token %} {{ formset.management_form }} 姓名 考勤 作业成绩 作业评语 {% for form in formset %} {{ form.id }} {{ form.instance.student }} {{ form.instance.get_record_display }} {{ form.score }} {{ form.homework_note }} {% endfor %} 四、inlineformset_factory

试想我们有如下recipe(菜谱)模型,Recipe(菜谱)与Ingredient(原料)是一对多的关系。一般的formset只允许我们一次性提交多个Recipe或多个Ingredient。但如果我们希望同一个页面上添加一个菜谱(Recipe)和多个原料(Ingredient),这时我们就需要用使用inlineformset了。

from django.db import models class Recipe(models.Model):    title = models.CharField(max_length=255)    description = models.TextField() class Ingredient(models.Model):    recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name='ingredient')    name = models.CharField(max_length=255)

利用inlineformset_factory创建formset的方法如下所示。该方法的第一个参数和第二个参数都是模型,其中第一个参数必需是ForeignKey。

# forms.py from django.forms import ModelForm from django.forms import inlineformset_factory from .models import Recipe, Ingredient, Instruction class RecipeForm(ModelForm):    class Meta:        model = Recipe        fields = ("title", "description",) IngredientFormSet = inlineformset_factory(Recipe, Ingredient, fields=('name',),                                          extra=3, can_delete=False, max_num=5)

views.py中使用formset创建和更新recipe(菜谱)的代码如下。在对IngredientFormSet进行实例化的时候,必需指定recipe的实例。

def recipe_update(request, pk): #更新    recipe = get_object_or_404(Recipe, pk=pk)    if requesthod == "POST":        form = RecipeForm(request.POST, instance=recipe)        if form.is_valid():            recipe = form.save()            ingredient_formset = IngredientFormSet(request.POST, instance=recipe)            if ingredient_formset.is_valid():                ingredient_formset.save()        return redirect('/recipe/')    else:        form = RecipeForm(instance=recipe)        ingredient_formset = IngredientFormSet(instance=recipe)    return render(request, 'recipe/recipe_update.html', {'form': form,                                                         'ingredient_formset': ingredient_formset,                                                      }) def recipe_add(request): #创建    if requesthod == "POST":        form = RecipeForm(request.POST)        if form.is_valid():            recipe = form.save()            ingredient_formset = IngredientFormSet(request.POST, instance=recipe)            if ingredient_formset.is_valid():                ingredient_formset.save()        return redirect('/recipe/')    else:        form = RecipeForm()        ingredient_formset = IngredientFormSet()    return render(request, 'recipe/recipe_add.html', {'form': form,                                                      'ingredient_formset': ingredient_formset,                                                      })

模板recipe/recipe_add.html代码如下:

Add Recipe    {% csrf_token %}        {{ form.as_p }}                Recipe Ingredient        {{ ingredient_formset.management_form }}        {{ ingredient_formset.non_form_errors }}        {% for form in ingredient_formset %}                {{ form.name.errors }}                {{ form.name.label_tag }}                {{ form.name }}                  {% endfor %}         整个formset的验证

formset由多个表单组成,单个表单的验证可以通过自定义的clean方法来完成,然而有时我们需要对整个formset的数据进行验证。一个常见例子就是去重。

比如下面例子中用户一次性提交多篇文章标题后,我们需要检查title是否已重复。我们先定义一个BaseFormSet,然后使用formset=BaseArticleFormSet添加formset的验证。

from django.forms import BaseFormSet from django.forms import formset_factory from myapp.forms import ArticleForm class BaseArticleFormSet(BaseFormSet): def clean(self): """Checks that no two articles have the same title.""" if any(self.errors): return titles = [] for form in self.forms: title = form.cleaned_data['title'] if title in titles: raise forms.ValidationError("Articles in a set must have distinct titles.") titles.append(title) ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)

给Formset添加额外字段

在BaseFormSet里我们不仅可以添加formset的验证,而且可以添加额外的字段,如下所示:

from django.forms import BaseFormSet from django.forms import formset_factory from myapp.forms import ArticleForm class BaseArticleFormSet(BaseFormSet): def add_fields(self, form, index): super().add_fields(form, index) form.fields["my_field"] = forms.CharField() ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)

小结

Formset真的非常有用,属于Django必备的基础知识之一。使用的时候先定义单个的form,然后利用factory生成formset。你需要根据不同应用场景选择不同的formset,并了解如何进行formset的验证。

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有